﻿using System;
using System.Collections.Generic;
using Common;
using IBll.Sys;
using System.IO;
using System.Runtime.Caching;
using Model.Sys;
using Model.Sys.Config;

//原先使用System.Web.Caching.Cache，但是asp.net会在System.Web.Caching.Cache缓存页面等数据，
//于是替换了System.Web.Caching.Cache为MemoryCache。
//而在使用MemoryCache的时候，重新启动网站会丢失缓存，于是加了自己的扩展，将缓存序列化存放在文件内，
//在缓存丢的时候从文件内获取缓存，做了简易的扩展。
//总而言之：为了缓存的“健壮性”
namespace Bll.Sys
{
    public class MemoryCacheBll:ICacheBll
    {
        public bool Add<T>(string key, T value)
        {
            Cache<T> cache=new Cache<T>();

            cache.CreateTime = DateTime.Now;
            cache.FailureTime = DateTime.MaxValue;
            cache.Value = value;
            return MemoryCacheHelper.AddCache(key, cache);
        }

        public bool Add<T>(string key, T value, DateTime expireTime)
        {
            Cache<T> cache=new Cache<T>();

            cache.CreateTime = DateTime.Now;
            cache.FailureTime = expireTime;
            cache.Value = value;
            return MemoryCacheHelper.AddCache(key, cache, expireTime);
        }

        public bool Remove(string key)
        {
            return MemoryCacheHelper.RemoveCache(key);
        }

        public bool Remove(List<string> keys)
        {
            foreach (var key in keys)
            {
                MemoryCacheHelper.RemoveCache(key);
            }
            return true;
        }

        public bool Remove()
        {
            return MemoryCacheHelper.RemoveCache();
        }

        public bool Update<T>(string key, T value)
        {
            return MemoryCacheHelper.UpdateCache(key, value);
        }

        public int GetCount()
        {
            return MemoryCacheHelper.GetCacheCount();
        }

        public bool IsExists(string key)
        {
            return MemoryCacheHelper.IsExistCache(key);
        }

        public Cache<T> GetCache<T>(string key)
        {
            return MemoryCacheHelper.GetCache<T>(key);
        }
    }








    public class MemoryCacheHelper
    {
        //主要防止外部程序访问,AppDomain.CurrentDomain.BaseDirectory:当前应用程序根目录(物理路径)
        private static string fileRootPath;
        //文件扩展名
        private static string fileExt;

        static MemoryCacheHelper()
        {
            InitializeStaticMember();
        }

        #region 初始化静态成员

        /// <summary>
        /// 初始化静态成员
        /// </summary>
        public static void InitializeStaticMember()
        {
            fileRootPath = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + CacheConfig.CacheFilePath);
            fileExt = CacheConfig.CacheFileExtensionName;
        }

        #endregion

        #region 增加缓存

        /// <summary>
        /// 设置数据缓存,永不过期（key即缓存文件名）
        /// </summary>
        public static bool AddCache(string key, object objObject)
        {
            string filepath = fileRootPath + key + fileExt;
            CacheItemPolicy policy = new CacheItemPolicy();

            policy.Priority = CacheItemPriority.NotRemovable;
            policy.AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration;
            //policy.RemovedCallback = a => File.Delete(filepath);//同步线程删除，会等待删除任务完成（注释掉：不然重启网站，去除缓存的同时，文件也没了）
            return AddCache(key, objObject, policy);
        }

        /// <summary>
        /// 设置数据缓存,自定义过期（key即缓存文件名）
        /// </summary>
        public static bool AddCache(string key, object objObject, DateTime expireTime)
        {
            string filepath = fileRootPath + key + fileExt;
            CacheItemPolicy policy = new CacheItemPolicy();

            policy.Priority = CacheItemPriority.NotRemovable;
            policy.AbsoluteExpiration = expireTime;
            //policy.RemovedCallback = a => File.Delete(filepath); //同步线程删除，会等待删除任务完成（注释掉：不然重启网站，去除缓存的同时，文件也没了）
            return AddCache(key, objObject, policy);
        }

        /// <summary>
        /// 设置数据缓存（增加和更新合并）
        /// </summary>
        public static bool AddCache(string key, object objObject, CacheItemPolicy policy)
        {
            string filepath = fileRootPath + key + fileExt;
            CacheItem cacheItem = new CacheItem(key, objObject);

            CacheToCacheFile(objObject, filepath);
            if (MemoryCache.Default.Get(key) != null)//如果已存在，则是更新
            {
                MemoryCache.Default.Remove(key);
            }
            return System.Runtime.Caching.MemoryCache.Default.Add(cacheItem, policy);//添加缓存项到MemoryCache
        }

        #endregion

        #region 删除缓存

        /// <summary>
        /// 移除指定数据缓存
        /// </summary>
        public static bool RemoveCache(string key)
        {
            string filepath = fileRootPath + key + fileExt;//缓存序列化的文件路径

            System.Runtime.Caching.MemoryCache.Default.Remove(key);
            //删除缓存文件
            if (File.Exists(filepath))
            {
                File.Delete(filepath);
            }
            return true;
        }

        /// <summary>
        /// 移除全部缓存
        /// </summary>
        public static bool RemoveCache()
        {
            MemoryCache cache = System.Runtime.Caching.MemoryCache.Default;
            foreach (var c in cache)
            {
                RemoveCache(c.Key);
            }
            return true;
        }

        #endregion

        #region 修改缓存

        /// <summary>
        /// 修改缓存对象
        /// </summary>
        public static bool UpdateCache<T>(string key, T value)
        {
            string filepath = fileRootPath + key + fileExt;
            CacheItem cacheItem = System.Runtime.Caching.MemoryCache.Default.GetCacheItem(key);

            if (cacheItem != null)
            {
                ((Cache<T>)cacheItem.Value).Value = value;//更新缓存
                CacheToCacheFile((Cache<T>)cacheItem.Value, filepath);//同步缓存文件
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion

        #region 查询缓存

        /// <summary>
        /// 获取数据缓存
        /// </summary>
        /// <param name="key">键</param>
        public static Cache<T> GetCache<T>(string key)
        {
            CacheItem cacheItem = System.Runtime.Caching.MemoryCache.Default.GetCacheItem(key);//根据键从MemoryCache中获取缓存
            if (cacheItem == null)
            {
                string filepath = fileRootPath + key + fileExt;//缓存序列化的文件路径
                if (File.Exists(filepath))
                {
                    FileStream _file = File.OpenRead(filepath);
                    if (_file.CanRead)
                    {
                        object obj = SerializeHelper.BinaryDeSerialize(_file);
                        Cache<T> cache = (Cache<T>)obj;
                        if (cache != null)
                        {
                            //判断是否过期
                            if (cache.FailureTime >= DateTime.Now)
                            {
                                //将数据添加到MomeryCache
                                CacheItem cacheItemNew = new CacheItem(key, cache);
                                CacheItemPolicy policy = new CacheItemPolicy();

                                policy.Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable;
                                policy.AbsoluteExpiration = cache.FailureTime;
                                //policy.RemovedCallback = a => File.Delete(filepath);//同步线程删除，会等待删除任务完成（注释掉：不然重启网站，去除缓存的同时，文件也没了）
                                System.Runtime.Caching.MemoryCache.Default.Add(cacheItemNew, policy);
                                return cache;
                            }
                            else
                            {
                                File.Delete(filepath);
                                //数据过期
                                return null;
                            }
                        }
                        else { return null; }
                    }
                    else { return null; }
                }
                else { return null; }
            }
            else
            {
                return (Cache<T>)cacheItem.Value;
            }
        }

        /// <summary>
        /// 缓存个数
        /// </summary>
        /// <returns></returns>
        public static int GetCacheCount()
        {
            return (int)System.Runtime.Caching.MemoryCache.Default.GetCount();
        }

        /// <summary>
        /// 缓存文件个数
        /// </summary>
        /// <returns></returns>
        public static object GetFileCacheCount()
        {
            DirectoryInfo di = new DirectoryInfo(fileRootPath);
            return di.GetFiles().Length;
        }

        /// <summary>
        /// 是否存在对应key值的缓存(只适用于永久有效的缓存)
        /// </summary>
        public static bool IsExistCache(string key)
        {
            bool result = MemoryCache.Default.Contains(key);
            if (result == false)
            {
                string filepath = fileRootPath + key + fileExt;//缓存序列化的文件路径
                if (File.Exists(filepath))
                {
                    result = true;
                }
            }

            return result;
        }

        #endregion

        #region 同步缓存到缓存文件（补丁）

        /// <summary>
        /// 同步缓存到缓存文件（MemoryCache重新启动网站会丢失）
        /// </summary>
        public static void CacheToCacheFile(object cache, string filepath)
        {
            FileStream stream = null;

            if (Directory.Exists(fileRootPath) == false)
            {
                Directory.CreateDirectory(fileRootPath);
            }
            if (File.Exists(filepath) == false)
            {
                stream = new FileStream(filepath, FileMode.CreateNew, FileAccess.Write, FileShare.Write);//创建对象的同时，也就创建了文件
            }
            else
            {
                stream = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.Write);
            }
            SerializeHelper.BinarySerialize(stream, cache);//序列化到缓存文件
        }

        #endregion

    }


}
